/**
 * BrandBigData.com Inc.
 * Copyright (c) 2018 All Rights Reserved.
 */
package com.qibao.toolkit.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Instant;

/**
 * @author HaoBin
 * @version $Id: MutilTenantIdGeneratorUtils.java, v0.1 2018/3/1 19:37 HaoBin
 */
public class MutilTenantIdGeneratorUtils {

    private static final Logger logger = LoggerFactory.getLogger(MutilTenantIdGeneratorUtils.class);
    /**
     * 机器id
     */
    private long workerId;
    /**
     * 序列号
     */
    private long sequence;

    /**
     * 起始时间戳
     */
    private long twepoch = 1519903309L;

    private long workerIdBits = 6L;
    private long sequenceBits = 12L;
    private long tenantIdBits = 14L;

    /**
     * workId最大值
     */
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private long maxTenantId = -1L ^ (-1L << tenantIdBits);

    /**
     * 或运算左移对齐位数
     */
    private long tenantIdShift = 0L;
    private long sequenceLeftShift = tenantIdBits;
    private long workIdShift = tenantIdBits + 12L;
    private long timestampLeftShift = tenantIdBits + 12L + workerIdBits;

    private long sequenceMask = -1L ^ (-1L << sequenceBits);
    private long lastTimestamp = 1L;

    public MutilTenantIdGeneratorUtils(long workerId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("workId不符合要求");
        }
        this.workerId = workerId;
    }

    /**
     * 生成id
     */

    public synchronized long nextId(Long tenantId) {
        long timestamp = timeGen();

        if (timestamp < lastTimestamp) {
            System.err.printf("时间不能小于上一次时间戳%d", lastTimestamp);
            throw new RuntimeException("时间戳小于上一秒时间戳");
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextSecond(lastTimestamp);
            }
        } else {
            sequence = 0;
        }

        lastTimestamp = timestamp;
        System.out.println("时间戳:");
        System.out.print(Long.toBinaryString((timestamp - twepoch) << timestampLeftShift));
        System.out.println();
        System.out.println("workid:");
        System.out.print(Long.toBinaryString(workerId << workIdShift));
        System.out.println();
        System.out.println("序列号");
        System.out.print(sequence);
        System.out.println();
        System.out.println("租户id");
        System.out.print(tenantId << tenantIdShift);
        System.out.println();
        long result = ((timestamp) << timestampLeftShift) | (workerId << workIdShift) | (sequence
                << sequenceLeftShift) | (tenantId << tenantIdShift);
        System.out.print(Long.toBinaryString(result));
        System.out.println();
        return result;
    }

    /**
     * 返回下一秒
     */
    private long tilNextSecond(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回当前秒
     */
    private long timeGen() {
        return Instant.now().getEpochSecond();
    }
}